﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Flatland.GeneticProgramming.CoreTypes;
using Flatland.GeneticProgramming.Crossovers;
using Flatland.GeneticProgramming.Mutations;
using Flatland.Utilities;

namespace Flatland.AgentBehavior {
    /// <summary>
    /// Agent genes class.
    /// </summary>
    sealed public class BehaviorFactory {
        /// <summary>
        /// Get the genes relative to each Layer.
        /// </summary>
        /// <returns></returns>
        public static List<Report> CreateBaseBehavior() {
            List<Report> genes = new List<Report>();
            foreach (FieldInfo fi in (from fi in typeof(Layer).GetFields(BindingFlags.Public | BindingFlags.Static)
                                      where Attribute.IsDefined(fi, typeof(Subsume))
                                      select fi)) {
                Subsume sg = (Subsume)Attribute.GetCustomAttribute(fi, typeof(Subsume));
                //Set the Layer on the genotype
                sg.Report.GenoType.Layer = (Layer)fi.GetRawConstantValue();
                genes.Add(sg.Report);
            }
            return genes;
        }

        /// <summary>
        /// Crosses and mutates two Behaviors. Returns one of the two.
        /// </summary>
        /// <param name="father"></param>
        /// <param name="mother"></param>
        /// <param name="gp"> </param>
        /// <returns></returns>
        public static List<Report> EvolveBehavior(Agent father, Agent mother, GeneticProgramming.GP gp) {
            if (father == null) throw new ArgumentNullException();
            if (mother == null) throw new ArgumentNullException();
            Mutation mutation = new Mutation(gp.MutationTechnique);
            Crossover crossOver = new Crossover(gp.CrossoverTechnique);
            int genes = father.Reports.Count;
            List<Report> newGenes = new List<Report>(genes);
            for (int i = 0; i < genes; i++) {
                Report r1 = (Report)father.Reports[i].Clone();
                Report r2 = (Report)mother.Reports[i].Clone();
                List<Report> reps = new List<Report> {r1, r2};
                crossOver.Cross(reps, gp.CrossOverRate, (int) gp.MaxTreeDepth);
                mutation.Mutate(reps, gp.MutationRate, (int)gp.MaxTreeDepth);
                mutation.Mutate(reps, gp.MutationRate,(int)gp.MaxTreeDepth);
                newGenes.Add(Utils.Random.Next(2) == 0 ? r1 : r2);
            }
            return newGenes;
        }
    }
}
